#include "time.h"

#include <utility>
#include <string>
#include <cstring>

/*
 * Constructors & destructor
 */

Time::Time() : secs(0), mins(0) {}

Time::Time(int mins, int secs) : secs(secs), mins(mins) {}

Time::Time(const Time &other) {
    this->secs = other.secs;
    this->mins = other.mins;
}

Time::~Time() {}

/*
 * Setters & getters
 */

void Time::setTime(int mins, int secs) {
    this->secs = secs;
    this->mins = mins;
}

void Time::setTime(std::pair<int, int> m_s) {
    this->secs = m_s.second;
    this->mins = m_s.first;
}

void Time::setSeconds(int secs) {
    this->secs = secs;
}

void Time::setMinutes(int mins) {
    this->mins = mins;
}

std::pair<int, int> Time::getTime() {
    return { this->mins, this->secs };
}

int Time::getSeconds() {
    return this->secs;
}

int Time::getMinutes() {
    return this->mins;
}

/*
 * Overloads
 */

Time & Time::operator=(const Time &other) {
    if(this == &other) return *this;

    this->secs = other.secs;
    this->mins = other.mins;
    return *this;
}

Time & Time::operator=(Time &&other) noexcept {
    if(this == &other) return *this;

    this->secs = std::exchange(other.secs, 0);
    this->mins = std::exchange(other.mins, 0);
    return *this;
}

Time Time::operator+(const Time &op) {
    Time res(this->mins + op.mins, this->secs + op.secs);
    res.mins += res.secs / 60;
    res.secs %= 60;
    return res;
}

Time Time::operator-(const Time &op) {
    if(*this < op) throw std::range_error("class Time cannot contain negative time");
    Time res(this->mins - op.mins, this->secs - op.secs);
    res.mins -= (60 - res.secs) / 60;
    res.secs = (60 + res.secs) % 60;

    return res;
}

Time & Time::operator+=(const Time &op) {
    *this = *this + op;
    return *this;
}

Time & Time::operator-=(const Time &op) {
    *this = *this - op;
    return *this;
}

bool Time::operator==(const Time &op) {
    return this->mins == op.mins && this->secs == op.secs;
}

bool Time::operator!=(const Time &op) {
    return this->mins != op.mins && this->secs == op.secs;
}

bool Time::operator<(const Time &op) {
    return this->mins < op.mins || this->mins == op.mins && this->secs < op.secs;
}

bool Time::operator>(const Time &op) {
    return this->mins > op.mins || this->mins == op.mins && this->secs > op.secs;
}

bool Time::operator<=(const Time &op) {
    return this->mins < op.mins || this->mins == op.mins && this->secs <= op.secs;
}

bool Time::operator>=(const Time &op) {
    return this->mins > op.mins || this->mins == op.mins && this->secs >= op.secs;
}

std::ostream & operator<<(std::ostream &os, const Time &time) {
    os << time.mins << ":" << time.secs;
    return os;
}

std::istream & operator>>(std::istream &is, Time &time) {
    std::string s;
    is >> s;
    const char *c_str = s.c_str();
    const char *end_c_str = c_str + std::strlen(c_str);
    char *endptr;
    const char *sep = std::strchr(c_str, ':');
    if(sep == NULL) {
        return is;
    }

    s[sep - c_str] = '\0';
    errno = 0;
    long int mins = std::strtol(c_str, &endptr, 10);
    if(errno == ERANGE || c_str == sep || endptr != sep || mins != static_cast<int>(mins)) {
        return is;
    }

    errno = 0;
    long int secs = std::strtol(sep + 1, &endptr, 10);
    if(errno == ERANGE || sep + 1 == end_c_str || endptr != end_c_str || secs != static_cast<int>(secs)) {
        return is;
    }

    time.secs = secs;
    time.mins = mins;
    return is;
}

/*
 * Global functions
 */

Time seconds(int secs) {
    return Time(0, secs);
}

Time minutes(int mins) {
    return Time(mins, 0);
}


